package cn.cjlmonster.hyouka_assistant.audio

import android.Manifest
import android.content.pm.PackageManager
import android.media.AudioFormat
import android.media.AudioRecord
import android.media.MediaRecorder
import android.media.audiofx.AcousticEchoCanceler
import android.media.audiofx.AutomaticGainControl
import android.media.audiofx.NoiseSuppressor
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import android.util.Log
import androidx.core.content.ContextCompat
import cn.cjlmonster.hyouka_assistant.WKPlugin
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.EventChannel.StreamHandler
import java.io.IOException


/**
 * AudioRecorder
 *
 * @author cjl
 * @date 2025/6/23 16:19
 */
object AudioRecorder : StreamHandler {

    private const val TAG = "录音测试"

    private fun log(txt: String) {
        Log.d(TAG, "${Thread.currentThread().name} - $txt")
    }

    private const val CHANNEL_NAME = "cn.cjlmonster.hyouka/audio"

    private var SAMPLE_RATE = 44100 // 采样频率
    private const val CHANNEL_CONFIG: Int = AudioFormat.CHANNEL_IN_MONO // 单声道
    private const val AUDIO_FORMAT: Int = AudioFormat.ENCODING_PCM_16BIT // PCM 16位

    private var eventChannel: EventChannel? = null
    private var eventSink: EventChannel.EventSink? = null
    private val mainHandler = Handler(Looper.getMainLooper())
    private var handlerThread: HandlerThread? = null
    private var eventHandler: Handler? = null

    @JvmStatic
    fun bindEventChannel(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
        log("bindEventChannel")
        eventChannel = EventChannel(flutterPluginBinding.binaryMessenger, CHANNEL_NAME).also {
            it.setStreamHandler(this)
        }
        handlerThread = HandlerThread("event-thread").apply {
            start()
            eventHandler = Handler(looper)
        }
    }

    @JvmStatic
    fun unbindEventChannel() {
        try {
            log("unbindEventChannel")
            // 释放Record
            isRecording = false
            audioRecord?.stop()
            audioRecord?.release()
            audioRecord = null
            // 释放EventChannel
            eventChannel?.setStreamHandler(null)
            eventChannel = null
            eventSink = null
            // 释放Handler
            handlerThread?.interrupt()
            handlerThread = null
            eventHandler?.removeCallbacksAndMessages(null)
            eventHandler = null
            mainHandler.removeCallbacksAndMessages(null)
        } catch (e: Exception) {
            log("unbindEventChannel: ${e.message}")
        }
    }

    override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
        if (arguments != null && arguments is Map<*, *>) {
            val sampleRate = arguments["sampleRate"]
            if (sampleRate != null && sampleRate is Int) {
                SAMPLE_RATE = sampleRate
            }
        }
        eventSink = events
        startRecord()
    }

    override fun onCancel(arguments: Any?) {
        stopRecord()
    }

    private val bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT)
    private var audioRecord: AudioRecord? = null
    private var isRecording = false

    @JvmStatic
    private fun enableSystemNoiseSuppression(audioRecord: AudioRecord) {
        val audioSessionId = audioRecord.audioSessionId
        if (NoiseSuppressor.isAvailable()) NoiseSuppressor.create(audioSessionId)
        if (AcousticEchoCanceler.isAvailable()) AcousticEchoCanceler.create(audioSessionId)
        if (AutomaticGainControl.isAvailable()) AutomaticGainControl.create(audioSessionId)
    }

    @JvmStatic
    fun createRecord() {
        eventHandler?.post {
            log("createRecord: ${audioRecord == null}")
            val isGrant = ContextCompat.checkSelfPermission(
                WKPlugin.context!!,
                Manifest.permission.RECORD_AUDIO
            ) == PackageManager.PERMISSION_GRANTED
            if (isGrant && audioRecord == null) {
                audioRecord = AudioRecord(
                    MediaRecorder.AudioSource.MIC,
                    SAMPLE_RATE,
                    CHANNEL_CONFIG,
                    AUDIO_FORMAT,
                    bufferSize
                )
                enableSystemNoiseSuppression(audioRecord!!)
            }
        }
    }

    @JvmStatic
    fun destroyRecord() {
        eventHandler?.post {
            try {
                log("destroyRecord")
                isRecording = false
                audioRecord?.stop()
                audioRecord?.release()
                audioRecord = null
            } catch (e: Exception) {
                log("destroyRecord: ${e.message}")
            }
        }
    }

    @JvmStatic
    private fun startRecord() {
        Thread {
            try {
                log("startRecord")
                isRecording = true
                audioRecord?.run {
                    startRecording()
                    val buffer = ByteArray(bufferSize)
                    while (isRecording) {
                        val read = read(buffer, 0, buffer.size)
                        // log("buffer: ${buffer.size}")
                        if (read > 0) {
                            val audioData = buffer.copyOf(read)
                            mainHandler.post { eventSink?.success(audioData) }
                        }
                    }
                    log("录音结束")
                }
            } catch (e: IOException) {
                log("录音报错: ${e.message}")
            }
        }.apply {
            name = "record-thread"
        }.start()
    }

    @JvmStatic
    private fun stopRecord() {
        eventHandler?.post {
            log("stopRecord")
            isRecording = false
            audioRecord?.stop()
        }
    }
}